package structure;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

import static java.lang.Math.exp;
import static java.lang.Math.pow;

import static test.SimulationTest.N;
import static utils.TimeUtils.changeStrignToDate;

//import structure.TranscationRoute;

/**
 * @author zhangjianzhong
 * @create 2021-11-19 21:21
 */
public class ListenerNode {
    public String id;
    public HashSet<String> neightourHas;//记录监听到的全部节点集

    public ArrayList<TranscationRoute> txListenSet;
    public HashMap<String, Transaction4Packet> txListenSetPacketByTx = new HashMap<>();//这里按照交易来对所有的监听事件分类存储

    public ListenerNode(String id, HashSet<String> neightourHas, ArrayList<TranscationRoute> txListenSet) {
        this.id = id;
        this.neightourHas = neightourHas;
        this.txListenSet = txListenSet;
    }

    /**
     * 将监听节点记录的交易转发集，重新整理成按交易为类的集合，即一笔交易为一个类，里面记录所有的转发来源和时间戳
     * 所有类组成一个Map
     */
    public void packetListenByTx(ArrayList<String> normalList) {//normalList是普通节点列表，用来找扩展权重向量的位置的

        for (TranscationRoute txR : this.txListenSet) {
            String transHash = txR.transHash;
            String fromID = txR.fromNode;
            //String routeTime = txR.routeTime;

//            int index=findElement(normalList,fromID);//!!!
//            if (index==-1){
//                System.out.println("设置交易的时间戳和fromID时，找不到插入的位置");
//            }
            if (!this.txListenSetPacketByTx.containsKey(transHash)) {//已经有这笔交易的转发记录了，则添加新<时间戳：源节点>
                this.txListenSetPacketByTx.put(transHash, new Transaction4Packet());
            }
            this.txListenSetPacketByTx.get(transHash).listAndWeight.add(txR);
        }
    }

    /**
     * 将时间戳转换为相对时间戳单位 ,操作：找寻最小时间戳，计算相对时间戳，时间戳排序，删除只保留N个，
     */
    public void changeTimestampToRelative() {
        //找到最小时间戳，顺便记录一下中间时间戳和Kopt
        for (Transaction4Packet value : this.txListenSetPacketByTx.values()) { //遍历每一笔交易，生成这笔交易的相对时间戳列表
            //计算所有转发事件中的最小时间戳
            LocalDateTime minTime = LocalDateTime.now();
            for (int i = 0; i < value.listAndWeight.size(); i++) {
                TranscationRoute route = value.listAndWeight.get(i);
                if (route != null) {
                    if (Duration.between(changeStrignToDate(route.routeTime), minTime).toMillis() > 0L) {
                        minTime = changeStrignToDate(route.routeTime);
                    }
                }
            }
            //计算每个转发事件的相对时间戳
            for (int i = 0; i < value.listAndWeight.size(); i++) {
                TranscationRoute route = value.listAndWeight.get(i);
                if (route != null) {
                    Long timestamp = Duration.between(minTime, changeStrignToDate(route.routeTime)).toMillis();
                    route.relavateTimeStamp = timestamp;
                }
            }
            //只保留前N个记录，其他的都删掉,先排序，再删除
            Collections.sort(value.listAndWeight, new Comparator<TranscationRoute>() {
                @Override // 升序
                public int compare(TranscationRoute o1, TranscationRoute o2) {
                    return (int) (o1.relavateTimeStamp - o2.relavateTimeStamp);
                }
            });
            while (value.listAndWeight.size() > N) {
                value.listAndWeight.remove(value.listAndWeight.size() - 1);
            }
            //计算中间时间戳
            Long midTimeStamp;
            int count = value.listAndWeight.size();
            if (count % 2 == 1) {
                midTimeStamp = value.listAndWeight.get(count / 2).relavateTimeStamp;
            } else {
                midTimeStamp = (value.listAndWeight.get(count / 2).relavateTimeStamp + value.listAndWeight.get(count / 2 - 1).relavateTimeStamp) / 2;
            }
            value.midTimeStamp = midTimeStamp;
            value.k = midTimeStamp / 0.8325546111577;//根号(-ln(0.5))
        }
    }

    /**
     * 计算权重,由于权重计算里面的一个系数需要考虑“中间时间戳”，且模拟器出来的结果时间戳不是有序的
     * 只能单独来一步计算
     */
    public void computeWeightVertor() {
        for (Transaction4Packet value : this.txListenSetPacketByTx.values()) { //遍历每一笔交易
            for (int i = 0; i < value.listAndWeight.size(); i++) {
                TranscationRoute route = value.listAndWeight.get(i);
                if(value.k==0.0d){
                    route.weight=1.0d;
                }else{
                    route.weight = exp(-pow(route.relavateTimeStamp / value.k, 2));
                }

            }
        }
    }

}
